package cn.stevenyang.services;

import cn.stevenyang.exceptions.VariableNotFoundException;
import cn.stevenyang.exceptions.VariablePathTooDeepException;

import java.util.Stack;

public class CallStackService {
    public Stack<MemoryService> getMemoryServices() {
        return memoryServices;
    }
    private final Stack<MemoryService> memoryServices;
    public CallStackService() {
        this.memoryServices = new Stack<>();
        this.memoryServices.push(new MemoryService());
    }
    public CallStackService(MemoryService globalStack) {
        this.memoryServices = new Stack<>();
        this.memoryServices.push(globalStack);
    }
    public void pushNewStack() {
        memoryServices.push(new MemoryService());
    }

    public MemoryService popCallStack() {
        if (memoryServices.size() == 1) {
            return memoryServices.peek();
        }
        return memoryServices.pop();
    }

    public MemoryService peekOfCallStack() {
        return memoryServices.peek();
    }

    public void setVariable(String path, Object value) throws VariablePathTooDeepException {
        Stack<MemoryService> cloned = (Stack<MemoryService>) memoryServices.clone();
        while (!cloned.isEmpty()) {
            MemoryService poped = cloned.pop();
            if (poped.hasDefinedVariable(path)) {
                poped.setVariable(path, value);
                return;
            }
        }
        getMemoryServices().peek().setVariable(path, value);
    }

    public Object getVariable(String path) throws VariableNotFoundException, VariablePathTooDeepException {
        Stack<MemoryService> cloned = (Stack<MemoryService>) memoryServices.clone();
        while (!cloned.isEmpty()) {
            MemoryService poped = cloned.pop();
            if (poped.hasDefinedVariable(path)) {
                return poped.getVariable(path);
            }
        }
        throw new VariableNotFoundException();
    }

    public Object getVariableOrNull(String path) throws VariableNotFoundException, VariablePathTooDeepException {
        Stack<MemoryService> cloned = (Stack<MemoryService>) memoryServices.clone();
        while (!cloned.isEmpty()) {
            MemoryService poped = cloned.pop();
            if (poped.hasDefinedVariable(path)) {
                return poped.getVariable(path);
            }
        }
        return null;
    }

    public Boolean hasDefinedVariable(String path) {
        Stack<MemoryService> cloned = (Stack<MemoryService>) memoryServices.clone();
        while (!cloned.isEmpty()) {
            MemoryService poped = cloned.pop();
            if (poped.hasDefinedVariable(path)) {
                return true;
            }
        }
        return false;
    }
}
